#ifndef __BUOLA_SCENE_CTRANSFORM_H__
#define __BUOLA_SCENE_CTRANSFORM_H__

#include <buola/scene.h>
#include <buola/mat.h>

namespace buola { namespace scene {

class CTransform : public UShared
{
protected:
    CTransform();
    CTransform(const std::string &pName);
    virtual ~CTransform();

public:
    const std::string &Name()               {   return mName;   }
    CTransform *Parent()                    {   return mParent; }
    void AddChild(PTransform pChild);
    void RemoveChild(PTransform pChild);
    void AddObject(PObject pObject);
    void RemoveObject(PObject pObject);
    void RemoveAllObjects();
    
    void SetName(const std::string &pName)
    {
        mName=pName;
    }
    
    const mat::CMat34d &GetLocalTransform()
    {
        if(!mLocalTransformValid)
            CalcLocalTransform();
        return mLocalTransform;
    }

    const mat::CMat34d &GetWorldTransform()
    {
        return mWorldTransform;
    }

    void Update();
    void Render(CRenderContext &pContext);
    void CollectPolygons(std::vector<mat::CVec3d> &pPoints,std::vector<mat::CVec3d> &pNormals,std::vector<int> &pFaces);

    bool IsAncestorOf(const PTransform &pOther) const;

    static PTransform Import(const io::CURI &pURI);
    
    template<typename tType,typename tFunc>
    intrusive_ptr<tType> FindDescendantOfType(tFunc &&pFunc);

    template<typename tFunc>
    void TraverseTree(tFunc &&pFunc);
    
    PTransform FindDescendant(const std::string &pName);
    void PrintTransformTree(int pDepth=0);
    
private:
    virtual void CalcLocalTransform()=0;

private:
    CTransform *mParent;
    std::vector<PTransform> mChildren;
    std::vector<PObject> mObjects;
    std::string mName;

protected:
    mat::CMat34d mLocalTransform;
    bool mLocalTransformValid;
    
private:
    mat::CMat34d mWorldTransform;
};

template<typename tType,typename tFunc>
intrusive_ptr<tType> CTransform::FindDescendantOfType(tFunc &&pFunc)
{
    if(tType *lThis=dynamic_cast<tType*>(this))
    {
        if(pFunc(lThis))
            return lThis;
    }
    
    for(const PTransform &c : mChildren)
    {
        if(intrusive_ptr<tType> lReturn=c->FindDescendantOfType<tType>(std::forward<tFunc>(pFunc)))
            return lReturn;
    }
    
    return nullptr;
}

template<typename tFunc>
void CTransform::TraverseTree(tFunc &&pFunc)
{
    pFunc(this);
    
    for(const PTransform &c : mChildren)
    {
        c->TraverseTree(std::forward<tFunc>(pFunc));
    }
}

/*namespace scene*/ } /*namespace buola*/ }

#endif
